<?php

namespace Simanx\Spes\Loader;

use Illuminate\Foundation\Application;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;

/**
 * 在指定文件夹中加载指定的Attribute
 * @package Simanx\Spes\Attribute
 */
abstract class Loader
{
    /**
     * @var Application
     */
    protected Application $app;

    protected array $paths;
    protected array $dirs;
    protected string $fileName = '*.php';

    /**
     * 包含的文件
     * @var array
     */
    private array $files = [];

    /**
     * 所有扫描出来的类
     * @var array
     */
    private array $classes = [];

    public function __construct(Application $app, array $config)
    {
        $this->app = $app;
        $this->paths = [];
        foreach (Arr::wrap($config['path']) as $path) {
            if (is_dir($path) || glob($path, (\defined('GLOB_BRACE') ? \GLOB_BRACE : 0) | \GLOB_ONLYDIR | \GLOB_NOSORT)) {
                $this->paths[] = $path;
            }
        }

        if (isset($config['name'])) {
            $this->fileName = $config['name'];
        } elseif (isset($config['suffix'])) {
            $this->fileName = '*' . $config['suffix'];
        } else {
            $this->fileName = '*.php';
        }
    }

    /**
     * Load的文件
     * @return array
     */
    public function files()
    {
        if ($this->files) {
            return $this->files;
        }

        if (empty($this->paths)) {
            return $this->files = [];
        }

        $this->files = iterator_to_array(Finder::create()
            ->files()
            ->in($this->paths)
            ->name($this->fileName));
        return $this->files;
    }

    /**
     * 获取所有文件夹
     * @param string|int|array $levels
     * @return array
     */
    public function dirs(string|int|array $levels = 0): array
    {
        if ($this->dirs) {
            return $this->dirs;
        }

        $this->dirs = iterator_to_array(Finder::create()
            ->directories()
            ->depth($levels)
            ->in($this->paths));
        return $this->dirs;
    }

    /**
     * 获取所有扫描到的类
     * @return array
     */
    public function classes()
    {
        if ($this->classes) {
            return $this->classes;
        }

        return array_map(function (\SplFileInfo $file) {
            return $this->classFromFile($file);
        }, $this->files());
    }

    /**
     * Extract the class name from the given file path.
     *
     * @param SplFileInfo $file
     * @return string
     */
    protected function classFromFile(SplFileInfo $file): string
    {
        $class = trim(Str::replaceFirst(base_path(), '', $file->getRealPath()), DIRECTORY_SEPARATOR);

        return str_replace(
            [DIRECTORY_SEPARATOR, ucfirst(basename(app()->path())) . '\\'],
            ['\\', app()->getNamespace()],
            ucfirst(Str::replaceLast('.php', '', $class))
        );
    }

    /**
     * 进行加载
     */
    public abstract function load();
}
